'use strict';

// var g_config = { sdpSemantics: 'unified-plan' }
// var g_config = { sdpSemantics: 'plan-b' }
var g_config = { }

const sleep = (timeountMS) => new Promise((resolve) => {
	setTimeout(resolve, timeountMS);
});

/*
{
}
*/
class Room {
	constructor(el) {
		this.members = new Array();
		this.el = el;
		this.published = false;
	}
	sendObj(o) {
		this.ws.send(JSON.stringify(o));
	}

	createChannel(id) {
		this.sendObj({
			method: "createChannel",
			id: id
		});
	}
	sendReceiverCandidate(candidate, id) {
		this.sendObj({
			method: "receiverCandidate",
			id: id,
			candidate: candidate
		});
	}
	sendSenderCandidate(candidate, id) {
		this.sendObj({
			method: "senderCandidate",
			id: id,
			candidate: candidate
		});
	}
	sendSenderOffer(offer, id) {
		this.sendObj({
			method: "offer",
			id: id,
			offer: offer
		});
	}
	sendReceiveAnswer(answer, id) {
		this.sendObj({
			method: "answer",
			id: id,
			answer: answer
		});
	}


	createReceiverNotify(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.createReceiver(data.offer);
			}
		});
	}
	createSenderNotify(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.createSender(this.stream);
			}
		});
	}

	receiverCandidateNotify(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.receiverCandidateNotify(data.candidate);
			}
		});
	}
	senderCandidateNotify(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.senderCandidateNotify(data.candidate);
			}
		});
	}
	offer(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.offerData(data.offer);
			}
		});
	}
	async answer(data) {
		this.members.forEach(m => {
			if (m.status.id == data.id) {
				m.answerData(data.answer);
			}
		});
	}






	startCheckStatus() {
		setInterval(() => {
			this.members.forEach(m => {
				if (m.status.publish) {
					if (m.receiver)
						return;
					this.createChannel(m.status.id);
				}
			});
		}, 5000);

	}

	publish(stream) {
		this.stream = stream;
		this.published = true;
		this.sendObj({
			method: 'publish',
			id: this.id
		});
	}
	getStatus(id) {
		this.id = id;
		this.sendObj({
			method: 'getStatus',
			id: id
		});
	}
	statusChanged(members) {
		let newMembers = new Array();
		let changedMembers = new Array();
		members.forEach(m => {
			if (m.id == this.id)
				return;
			let exist = false;
			for (let i = 0; i < this.members.length; i++) {
				if (m.id == this.members[i].id) {
					exist = true;
					this.members[i].status = m;
					break;
				}
			}
			if (!exist) {
				let vl = document.createElement('video');
				vl.playsinline='playsinline';
				vl.autoplay='autoplay';
				vl.muted='muted';
				this.el.appendChild(vl);

				let nm = new Member(vl, m, (offer, id) => this.sendSenderOffer(offer, id), (answer, id) => this.sendReceiveAnswer(answer, id),
					(candidate, id) => this.sendSenderCandidate(candidate, id), (candidate, id) => this.sendReceiverCandidate(candidate, id)

				);
				this.members.push(nm);
			}

		})


	}
	join(id) {
		var ws = new WebSocket("ws://192.168.1.111:3000");
		this.ws = ws;
		ws.onopen = () => {
			//当WebSocket创建成功时，触发onopen事件
			console.log("open");
			this.getStatus(id);
		}
		ws.onmessage = (e) => {
			//当客户端收到服务端发来的消息时，触发onmessage事件，参数e.data包含server传递过来的数据
			let cmd = JSON.parse(e.data);
			switch (cmd.method) {
				case 'statusChanged':
					this.statusChanged(cmd.data);
					break;
				case 'createSenderNotify':
					this.createSenderNotify(cmd.data);
					break;
				case 'createReceiverNotify':
					this.createReceiverNotify(cmd.data);
					break;
				case 'receiverCandidateNotify':
					this.receiverCandidateNotify(cmd.data);
					break;
				case 'senderCandidateNotify':
					this.senderCandidateNotify(cmd.data);
					break;

				case 'offer':
					this.offer(cmd.data);
					break;

				case 'answer':
					this.answer(cmd.data);
					break;
				default:
					break;
			}
			console.log(e.data);
		}
		ws.onclose = (e) => {
			//当客户端收到服务端发送的关闭连接请求时，触发onclose事件
			console.log("close");
			this.ws = null;
		}
		ws.onerror = (e) => {
			//如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
			console.log(e);
			this.ws = null;
		}
		this.startCheckStatus();
	}

	async sleep() {
		await sleep(5000);
	}

	toString() {
		return '(' + this.x + ', ' + this.y + ')';
	}
}


class Member {
	constructor(el, status, offerSender, answerSender, senderCandidate, receiverCandidate) {
		this.el = el;
		this.status = status;
		this.id = status.id;
		this.offerSender = offerSender;
		this.answerSender = answerSender;
		this.senderCandidate = senderCandidate;
		this.receiverCandidate = receiverCandidate;
	}
	async sleep() {
		await sleep(5000);
	}
	async createSender(stream) {
		this.sender = new Sender(this.id, g_config, this.offerSender, this.senderCandidate);
		await this.sender.start(stream);
	}
	async createReceiver(offer) {
		this.receiver = new Receiver(this.id, g_config, this.el, this.answerSender, this.receiverCandidate);
		await this.receiver.start(offer);
	}


	receiverCandidateNotify(candidate) {
		this.receiver.addCandidate(candidate);
	}
	senderCandidateNotify(candidate) {
		this.sender.addCandidate(candidate);
	}
	offerData(offer) {

	}
	answerData(answer) {
		this.sender.answerData(answer);
	}




	toString() {
		return '(' + this.x + ', ' + this.y + ')';
	}
}

class Sender {
	constructor(id, config, offerSender, candidateSender) {
		this.id = id;
		this.config = config;
		this.offerSender = offerSender;
		this.candidateSender = candidateSender;
	}
	async addCandidate(candidate){
		await this.pc.addIceCandidate(candidate);

	}
	async start(localStream) {
		const videoTracks = localStream.getVideoTracks();
		const audioTracks = localStream.getAudioTracks();

		if (videoTracks && videoTracks.length > 0) {
			console.log(`Using video device: ${videoTracks[0].label}`);
		}
		if (audioTracks && audioTracks.length > 0) {
			console.log(`Using audio device: ${audioTracks[0].label}`);
		}

		this.pc = new RTCPeerConnection(this.config);
		console.log('Created local peer connection object pc1');
		this.pc.addEventListener('icecandidate', e => {
			if (e.candidate) {
				//todo submit candidate
				this.candidateSender(e.candidate, this.id);
			}

		});

		console.log('Created remote peer connection object pc2');

		this.pc.addEventListener('iceconnectionstatechange', e => {
			console.log(`ICE state: ${this.pc.iceConnectionState}`);
			console.log('ICE state change event: ', e);
		});



		localStream.getTracks().forEach(track => this.pc.addTrack(track, localStream));
		console.log('Added local stream to pc1');

		try {
			console.log('pc1 createOffer start');
			const offerOptions = {
				offerToReceiveAudio: 1,
				offerToReceiveVideo: 1
			};
			const offer = await this.pc.createOffer(offerOptions);
			// todo sumit offer	
			this.offerSender(offer, this.id);
			await this.pc.setLocalDescription(offer);

		} catch (e) {
			//onCreateSessionDescriptionError(e);
		}
	}
	async sleep() {
		await sleep(5000);
	}
	async answerData(answer) {
		await this.pc.setRemoteDescription(answer);
	}
	toString() {
		return '(' + this.x + ', ' + this.y + ')';
	}
}
/*
{  
}
*/

class Receiver {
	//config  {sdpSemantics: option.value}
	constructor(id, config, el, answerSender, candidateSender) {
		this.id = id;
		this.config = config;
		this.el = el;
		this.answerSender = answerSender;
		this.candidateSender = candidateSender;

	}
	async addCandidate(candidate){
		await this.pc.addIceCandidate(candidate);

	}
	async start(desc) {
		this.pc = new RTCPeerConnection(this.config);
		this.pc.addEventListener('icecandidate', e => {
			if (e.candidate) {
				//todo submit candidate
				this.candidateSender(e.candidate, this.id);
			}

		});

		this.pc.addEventListener('iceconnectionstatechange', e => {
			console.log(`ICE state: ${this.pc.iceConnectionState}`);
			console.log('ICE state change event: ', e);
		});
		this.pc.addEventListener('track', (e) => {
			if (this.el.srcObject !== e.streams[0]) {
				this.el.srcObject = e.streams[0];
				console.log('pc2 received remote stream');
			}
		});
		try {
			await this.pc.setRemoteDescription(desc);
			//onSetRemoteSuccess(pc2);
		} catch (e) {
			//onSetSessionDescriptionError();
		}

		console.log('pc2 createAnswer start');
		// Since the 'remote' side has no media stream we need
		// to pass in the right constraints in order for it to
		// accept the incoming offer of audio and video.
		try {
			const answer = await this.pc.createAnswer();
			// todo submit answer
			this.answerSender(answer, this.id);
			console.log('pc2 setLocalDescription start');
			await this.pc.setLocalDescription(answer);

		} catch (e) {
			//onCreateSessionDescriptionError(e);
		}

	}
	async sleep() {
		await sleep(5000);
	}

	toString() {
		return '';
	}
}

